package com.xzq.erp.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xzq.erp.constants.JwtConstants;
import com.xzq.erp.constants.RoleConstants;
import com.xzq.erp.domain.dto.LoginDTO;
import com.xzq.erp.domain.dto.RegisterDTO;
import com.xzq.erp.domain.po.SysRole;
import com.xzq.erp.domain.po.SysUser;
import com.xzq.erp.domain.po.SysUserRole;
import com.xzq.erp.domain.vo.LoginVO;
import com.xzq.erp.domain.vo.UserRoleVO;
import com.xzq.erp.domain.vo.WxRefreshVO;
import com.xzq.erp.enums.ResponseCodeEnum;
import com.xzq.erp.exception.BizException;
import com.xzq.erp.mapper.SysRoleMapper;
import com.xzq.erp.mapper.SysUserMapper;
import com.xzq.erp.mapper.SysUserRoleMapper;
import com.xzq.erp.service.IAuthService;
import com.xzq.erp.service.ISysRoleService;
import com.xzq.erp.service.ISysUserRoleService;
import com.xzq.erp.utils.JwtTool;
import com.xzq.erp.utils.WebUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.BooleanUtils;
import org.assertj.core.util.Lists;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 认证服务实现类
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class AuthServiceImpl implements IAuthService {

    private final AuthenticationManager authenticationManager;
    private final JwtTool jwtTool;
    private final SysUserMapper userMapper;
    private final SysRoleMapper roleMapper;
    private final SysUserRoleMapper userRoleMapper;
    private final ISysRoleService roleService;
    private final ISysUserRoleService userRoleService;
    private final PasswordEncoder passwordEncoder;

    @Override
    public LoginVO login(LoginDTO loginDTO) {
        try {
            SysUser user = userMapper.selectOne(
                    new LambdaQueryWrapper<SysUser>()
                            .eq(SysUser::getUsername, loginDTO.getUsername())
            );
            if(Objects.isNull(user)){
                throw new BizException(ResponseCodeEnum.USERNAME_NOT_EXISTS);
            }
            if(!passwordEncoder.matches(loginDTO.getPassword(), user.getPassword())){
                throw new BizException(ResponseCodeEnum.PASSWORD_ERROR);
            }
            // 生成JWT令牌
            Integer userId = user.getId();
            // 生成登录和刷新令牌
            String token = generateToken(Long.valueOf(userId), loginDTO.getRememberMe());
            // 获取权限列表
            List<String> permissions = Lists.newArrayList();
            List<UserRoleVO> userRoleVOList = userRoleService.getUserRoleVOList(Collections.singletonList(user.getId()));
            if(!CollectionUtils.isEmpty(userRoleVOList)){
                for(UserRoleVO userRoleVO : userRoleVOList){
                    permissions.add(userRoleVO.getRoleName());
                }
                List<Integer> roleIds = userRoleVOList.stream().map(UserRoleVO::getRoleId).collect(Collectors.toList());
                List<String> roleIdsPermissions = roleService.getRoleIdsPermissions(roleIds);
                permissions.addAll(roleIdsPermissions);
            }
            // 返回登录结果
            return LoginVO.builder()
                .token(token)
                .username(user.getUsername())
                .nickname(user.getNickname())
                .permissions(permissions)
                .build();
                
        } catch (AuthenticationException e) {
            log.error("登录失败: {}", e.getMessage());
            throw new BizException(ResponseCodeEnum.LOGIN_ERROR);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer register(RegisterDTO registerDTO) {
        // 检查用户名是否已存在
        Integer count = userMapper.selectCount(
            new LambdaQueryWrapper<SysUser>()
                .eq(SysUser::getUsername, registerDTO.getUsername())
        ).intValue();
        if (count > 0) {
            throw new BizException(ResponseCodeEnum.USERNAME_ALREADY_EXISTS);
        }
        
        // 创建新用户
        SysUser user = new SysUser();
        user.setUsername(registerDTO.getUsername());
        user.setPassword(passwordEncoder.encode(registerDTO.getPassword()));
        user.setNickname(registerDTO.getNickname());
        user.setStatus(true); // 默认启用
        user.setCreateTime(LocalDateTime.now());
        user.setUpdateTime(LocalDateTime.now());
        
        userMapper.insert(user);
        // 设置默认角色(普通员工)
        LambdaQueryWrapper<SysRole> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SysRole::getName, RoleConstants.ROLE_STAFF);
        queryWrapper.select(SysRole::getId);
        SysRole role = roleMapper.selectOne(queryWrapper);
        userRoleMapper.insert(new SysUserRole().setRoleId(role.getId()).setUserId(user.getId()));
        return user.getId();
    }

    @Override
    public String refreshToken(String refreshToken) {
        try{
            // 解析刷新token
            Long userId = jwtTool.parseRefreshToken(refreshToken);
            // 重新生成登录token和刷新token
            return generateToken(userId, false);
        }
        catch (Exception e){
            throw new BizException(ResponseCodeEnum.UNAUTHORIZED);
        }
    }

    @Override
    public void logout(HttpServletResponse response) {
        // 清空认证用户信息
        SecurityContextHolder.clearContext();
        // 清除当前用户刷新令牌的jti 本质让令牌无效
        jwtTool.cleanJtiCache();
        // 清除刷新令牌
        WebUtils.cookieBuilder()
                .name(JwtConstants.REFRESH_HEADER)
                .value("")
                .maxAge(0) // 0表示立即删除
                .build();
    }

    @Override
    public LoginVO WxLogin(LoginDTO loginDTO) {
        try {
            SysUser user = userMapper.selectOne(
                    new LambdaQueryWrapper<SysUser>()
                            .eq(SysUser::getUsername, loginDTO.getUsername())
            );
            if(Objects.isNull(user)){
                throw new BizException(ResponseCodeEnum.USERNAME_NOT_EXISTS);
            }
            if(!passwordEncoder.matches(loginDTO.getPassword(), user.getPassword())){
                throw new BizException(ResponseCodeEnum.PASSWORD_ERROR);
            }
            // 生成JWT令牌
            Integer userId = user.getId();
            // 生成登录token
            String token = jwtTool.createToken(Long.valueOf(userId));
            // 创建刷新token
            String refreshToken = jwtTool.createRefreshToken(Long.valueOf(userId), loginDTO.getRememberMe());
            // 获取权限列表
            List<String> permissions = Lists.newArrayList();
            List<UserRoleVO> userRoleVOList = userRoleService.getUserRoleVOList(Collections.singletonList(user.getId()));
            if(!CollectionUtils.isEmpty(userRoleVOList)){
                for(UserRoleVO userRoleVO : userRoleVOList){
                    permissions.add(userRoleVO.getRoleName());
                }
                List<Integer> roleIds = userRoleVOList.stream().map(UserRoleVO::getRoleId).collect(Collectors.toList());
                List<String> roleIdsPermissions = roleService.getRoleIdsPermissions(roleIds);
                permissions.addAll(roleIdsPermissions);
            }
            // 返回登录结果
            return LoginVO.builder()
                    .token(token)
                    .refreshToken(refreshToken)
                    .username(user.getUsername())
                    .nickname(user.getNickname())
                    .permissions(permissions)
                    .build();

        } catch (AuthenticationException e) {
            log.error("微信登录失败: {}", e.getMessage());
            throw new BizException(ResponseCodeEnum.LOGIN_ERROR);
        }
    }

    @Override
    public WxRefreshVO wxRefreshToken(String refreshToken) {
        try{
            // 解析刷新token
            Long userId = jwtTool.parseRefreshToken(refreshToken);
            // 重新生成登录token和刷新token
            // 生成登录token
            String token = jwtTool.createToken(userId);
            // 创建刷新token
            String newRefreshToken = jwtTool.createRefreshToken(userId, false);
            return WxRefreshVO.builder()
                    .token(token)
                    .refreshToken(newRefreshToken)
                    .build();
        }
        catch (Exception e){
            throw new BizException(ResponseCodeEnum.UNAUTHORIZED);
        }
    }

    /**
     * 生成登录token和刷新token
     *
     * @param userId
     * @param rememberMe
     * @return
     */
    private String generateToken(Long userId, Boolean rememberMe) {
        // 生成登录token
        String token = jwtTool.createToken(userId);
        // 创建刷新token
        String refreshToken = jwtTool.createRefreshToken(userId, rememberMe);
        log.info("generate token: {}", token);
        log.info("generate refresh token: {}", refreshToken);
        // 将refreshToken保存到用户Cookie中,并设置httpOnly为true 防止xxs脚本攻击
        // 如果是记住我，将Cookie保存到用户浏览器中7天，默认当前会话有效 关闭浏览器自动清空RefreshToken
        int maxAge = BooleanUtils.isTrue(rememberMe) ?
                (int) JwtConstants.JWT_REMEMBER_ME_TTL.toSeconds() : -1;
        WebUtils.cookieBuilder()
                .name(JwtConstants.REFRESH_HEADER)
                .value(refreshToken)
                .maxAge(maxAge)
                .build();
        return token;
    }
} 